package com.hwz.gulimall.search.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.shaded.com.google.gson.Gson;
import com.hwz.common.to.es.SkuEsModel;
import com.hwz.common.utils.R;
import com.hwz.gulimall.search.constant.EsConstant;
import com.hwz.gulimall.search.entity.AttrEntity;
import com.hwz.gulimall.search.entity.BrandEntity;
import com.hwz.gulimall.search.feign.ProductFeginService;
import com.hwz.gulimall.search.service.MallSearchService;
import com.hwz.gulimall.search.vo.SearchParam;
import com.hwz.gulimall.search.vo.SearchResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.Nested;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;



/**
 * @Author: hwz
 * @CreateTime: 2025-07-29
 */

@Service
@Slf4j
public class MallSearchServiceImpl implements MallSearchService {

    @Autowired
    RestHighLevelClient restHighLevelClient;
    @Autowired
    ProductFeginService productFeginService;



    //搜索产品数据
    @Override
    public SearchResult search(SearchParam searchParam) {

        //1、封装查询条件
        SearchRequest searchRequest = buildSearchRequest(searchParam);
        //2、发送查询
        SearchResponse response;
        try {
            response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        //3、组装查询结果
        SearchResult searchResult = buildSearchResult(response, searchParam);


        return searchResult;
    }

    private SearchResult buildSearchResult(SearchResponse response, SearchParam searchParam) {
        Gson gson = new Gson();
        SearchResult searchResult = new SearchResult();
        SearchHit[] hits = response.getHits().getHits();
        List<String> filterAttr = new ArrayList<>();

        //1、封装商品数据
        List<SkuEsModel> products = new ArrayList<>();
        for (SearchHit hit : hits) {
            SkuEsModel skuEsModel = gson.fromJson(hit.getSourceAsString(), SkuEsModel.class);
            if (!StringUtils.isEmpty(searchParam.getKeyword())){
                String skuTitle = hit.getHighlightFields().get("skuTitle").getFragments()[0].string();
                skuEsModel.setSkuTitle(skuTitle);
            }

            products.add(skuEsModel);
        }
        searchResult.setProducts(products);

        //2、封装分页信息
        searchResult.setTotal(response.getHits().getTotalHits().value);
        searchResult.setPageNum(EsConstant.PRODUCT_PAGESIZE);
        searchResult.setTotalPages((int)response.getHits().getTotalHits().value%EsConstant.PRODUCT_PAGESIZE == 0 ? (int)response.getHits().getTotalHits().value/EsConstant.PRODUCT_PAGESIZE : (int)response.getHits().getTotalHits().value/EsConstant.PRODUCT_PAGESIZE+1);


        //3、封装品牌信息
        ParsedLongTerms brandAgge = response.getAggregations().get("brand_agge");
        List<? extends Terms.Bucket> buckets = brandAgge.getBuckets();
        List<SearchResult.BrandVo> brandVos = new ArrayList<>();
        for (Terms.Bucket bucket : buckets) {
            String key = bucket.getKeyAsString();
            SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
            brandVo.setBrandId(Long.parseLong(key));
            //获取子聚合
            ParsedStringTerms brandIdAgg = bucket.getAggregations().get("brand_name_agge");
            String brandName = brandIdAgg.getBuckets().get(0).getKeyAsString();
            brandVo.setBrandName(brandName);
            ParsedStringTerms brandImgAgg = bucket.getAggregations().get("brand_img_agge");
            String brandImg = brandImgAgg.getBuckets().get(0).getKeyAsString();
            brandVo.setBrandImg(brandImg);
            brandVos.add(brandVo);
        }
        searchResult.setBrands(brandVos);

        //4、封装分类信息
        ParsedLongTerms catalogAgge = response.getAggregations().get("catalog_agge");
        List<? extends Terms.Bucket> catalogBuckets = catalogAgge.getBuckets();
        List<SearchResult.CatalogVo> catalogVos = new ArrayList<>();
        for (Terms.Bucket bucket : catalogBuckets) {
            String key = bucket.getKeyAsString();
            SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
            catalogVo.setCatalogId(Long.parseLong(key));
            //获取子聚合
            ParsedStringTerms catalogNameAgge = bucket.getAggregations().get("catalog_name_agge");
            String catalogName = catalogNameAgge.getBuckets().get(0).getKeyAsString();
            catalogVo.setCatalogName(catalogName);
            catalogVos.add(catalogVo);
        }
        searchResult.setCatalogs(catalogVos);

        //5、封装属性信息
        Nested nestedAgg = response.getAggregations().get("attr_nested");
        ParsedLongTerms attrIdAgge = nestedAgg.getAggregations().get("attr_id_agge");
        List<? extends Terms.Bucket> attrIdBuckets = attrIdAgge.getBuckets();
        List<SearchResult.AttrVo> attrVos = new ArrayList<>();
        for (Terms.Bucket attrIdBucket : attrIdBuckets) {
            String keyAsString = attrIdBucket.getKeyAsString();
            SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
            attrVo.setAttrId(Long.parseLong(keyAsString));
            
            ParsedStringTerms attrNameAgge = attrIdBucket.getAggregations().get("attr_name_agge");
            attrVo.setAttrName(attrNameAgge.getBuckets().get(0).getKeyAsString());


            ParsedStringTerms attrValueAgge = attrIdBucket.getAggregations().get("attr_value_agge");
            List<? extends Terms.Bucket> valueAggeBuckets = attrValueAgge.getBuckets();
            List<String> values = new ArrayList<>();
            for (Terms.Bucket value : valueAggeBuckets) {
                values.add(value.getKeyAsString());
            }
            attrVo.setAttrValue(values);
            attrVos.add(attrVo);
        }
        searchResult.setAttrs(attrVos);

        List<Integer> pageNavs = new ArrayList<>();
        for (int i = 0; i < searchResult.getTotalPages(); i++) {
            pageNavs.add(i+1);
        }
        searchResult.setPageNavs(pageNavs);


        //6、封装面包屑数据
        List<Long> attrIds ;
        if (!CollectionUtils.isEmpty(searchParam.getAttrs())){
            attrIds = searchParam.getAttrs().stream().filter(i-> !StringUtils.isEmpty(i)).map(attr -> {
                String[] split = attr.split("_");
                return Long.parseLong(split[0]);
            }).distinct().collect(Collectors.toList());


            R r = productFeginService.infoList(attrIds);
            if (r.getCode() == 0) {
                List<AttrEntity> data = (List<AttrEntity>) r.getTargetData("list",  new TypeReference<List<AttrEntity>>(){} );

                Map<Long, AttrEntity> map = data.stream().collect(Collectors.toMap(AttrEntity::getAttrId, attrEntity -> attrEntity));
                List<SearchResult.NavVo> collect = searchParam.getAttrs().stream().map(attr -> {
                    String[] split = attr.split("_");
                    SearchResult.NavVo navVo = new SearchResult.NavVo();
                    AttrEntity attrEntity = map.get(Long.parseLong(split[0]));
                    navVo.setNavName(attrEntity.getAttrName());
                    navVo.setNavValue(split[1]);
                    String encode ="";
                    filterAttr.add(attrEntity.getAttrId() + "_" + split[1]);
                    try {
                        encode  = URLEncoder.encode(attr, "UTF-8");
                        encode = encode.replace("+", "%20");
                    } catch (UnsupportedEncodingException e) {
                        throw new RuntimeException(e);
                    }
                    String replace = searchParam.get_queryString().replace("&attrs=" + encode, "");
                    navVo.setLink("http://search.gulimall.com/list.html?" + replace);


                    return navVo;
                }).collect(Collectors.toList());

                searchResult.setNavs(collect);

            }
        }



        //7、封装品牌的面包屑
        if (!CollectionUtils.isEmpty(searchParam.getBrandId())){
//            SearchResult.NavVo navVo = new SearchResult.NavVo();
            List<SearchResult.NavVo> navs = searchResult.getNavs();
            R brandR = productFeginService.brandInfoList(searchParam.getBrandId());
            if (brandR.getCode() == 0) {
                List<BrandEntity> data = (List<BrandEntity>) brandR.getTargetData("list",  new TypeReference<List<BrandEntity>>(){} );

                Map<Long, BrandEntity> map = data.stream().collect(Collectors.toMap(BrandEntity::getBrandId, brandEntity -> brandEntity));

                List<SearchResult.NavVo> collect = searchParam.getBrandId().stream().map(brandId -> {
                    SearchResult.NavVo navVo = new SearchResult.NavVo();
                    BrandEntity brandEntity = map.get(brandId);
                    navVo.setNavName("品牌");
                    navVo.setNavValue(brandEntity.getName());
                    filterAttr.add(brandEntity.getBrandId().toString());

                    navVo.setLink("http://search.gulimall.com/list.html?" + searchParam.get_queryString().replace("&brandId=" + brandId, ""));
                    return navVo;

                }).collect(Collectors.toList());
                navs.addAll(collect);
                searchResult.setNavs(navs);

            }


        }

        searchResult.setFilterAttr(filterAttr);

        return searchResult;

    }

    private SearchRequest buildSearchRequest(SearchParam searchParam) {


        //1、封装查询条件
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); //构建dsl语句
        //先套一个boolQuery
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //1.1 skuTiile 商品标题
        if (!StringUtils.isEmpty(searchParam.getKeyword())){
            boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle",searchParam.getKeyword()));
        }
        //1.2 分类id
        if (!StringUtils.isEmpty(searchParam.getCatalog3Id())){
            boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId",searchParam.getCatalog3Id()));
        }
        //1.3 hasStock 是否有库存
        if (searchParam.getHasStock()!=null){
            boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock",searchParam.getHasStock()==1));
        }
        //1.4 品牌id
        if (!CollectionUtils.isEmpty(searchParam.getBrandId())) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId",searchParam.getBrandId()));
        }

        //1.5 价格区间
        if (!StringUtils.isEmpty(searchParam.getSkuPrice())){
            String[] split = searchParam.getSkuPrice().split("_");
            if (split.length==2){
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice").gte(split[0]).lte(split[1]));
            } else if (split.length==1) {
                if (searchParam.getSkuPrice().startsWith("_")){
                    boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice").lte(split[0]));
                } else if (searchParam.getSkuPrice().endsWith("_")) {
                    boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice").gte(split[0]));
                }
            }

        }
        //1.6 属性条件
        if (!CollectionUtils.isEmpty(searchParam.getAttrs())) {
            for (String attr : searchParam.getAttrs()) {
                BoolQueryBuilder nesteboolQueryBuilder = QueryBuilders.boolQuery();
                String[] split = attr.split("_");
                nesteboolQueryBuilder.must(QueryBuilders.termQuery("attrs.attrId",split[0]));
                String[] valueSplit = split[1].split(":");
                nesteboolQueryBuilder.must(QueryBuilders.termsQuery("attrs.attrValue",valueSplit));
                NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery("attrs", nesteboolQueryBuilder, ScoreMode.None);
                boolQueryBuilder.filter(nestedQuery);
            }
        }


        sourceBuilder.query(boolQueryBuilder);




        //2、
        //2.1 排序
        if (!StringUtils.isEmpty(searchParam.getSort())) {
            String[] split = searchParam.getSort().split("_");
            SortOrder sortOrder = split[1].equalsIgnoreCase("asc")? SortOrder.ASC: SortOrder.DESC;
            sourceBuilder.sort(split[0],sortOrder);
        }

        //2.2 分页
        sourceBuilder.from((searchParam.getPageNum()-1)*EsConstant.PRODUCT_PAGESIZE); //计算分页的起始
        sourceBuilder.size(EsConstant.PRODUCT_PAGESIZE);


        //2.3 高亮
        if (!StringUtils.isEmpty(searchParam.getKeyword())) {
            sourceBuilder.highlighter(new HighlightBuilder().field("skuTitle").preTags("<b style='color:red'>").postTags("</b>"));
        }




        //3、设置聚合条件
        //3.1品牌聚合
        TermsAggregationBuilder brandAgge = AggregationBuilders.terms("brand_agge");
        brandAgge.field("brandId").size(50);
        brandAgge.subAggregation(AggregationBuilders.terms("brand_name_agge").field("brandName").size(1));
        brandAgge.subAggregation(AggregationBuilders.terms("brand_img_agge").field("brandImg").size(1));
        sourceBuilder.aggregation(brandAgge);
        //3.2分类聚合
        TermsAggregationBuilder catalogAgge = AggregationBuilders.terms("catalog_agge");
        catalogAgge.field("catalogId").size(20);
        catalogAgge.subAggregation(AggregationBuilders.terms("catalog_name_agge").field("catalogName").size(1));
        sourceBuilder.aggregation(catalogAgge);
        //3.3属性聚合
        NestedAggregationBuilder attrAgge = AggregationBuilders.nested("attr_nested", "attrs");
        TermsAggregationBuilder attrIdAgge = AggregationBuilders.terms("attr_id_agge").field("attrs.attrId");
        attrIdAgge.subAggregation(AggregationBuilders.terms("attr_name_agge").field("attrs.attrName").size(1));
        attrIdAgge.subAggregation(AggregationBuilders.terms("attr_value_agge").field("attrs.attrValue").size(50));
        attrAgge.subAggregation(attrIdAgge);
        sourceBuilder.aggregation(attrAgge);

        System.out.println(sourceBuilder.toString());




        SearchRequest searchRequest = new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX}, sourceBuilder);


        return searchRequest;
    }
}
